# auto load
extends Node


#region signals
signal before_calc_damage(info: DamageInfo)
signal after_calc_damage(info: DamageInfo)
signal apply_damage(info: DamageInfo)
signal miss_damage(info: DamageInfo)
#endregion


func _ready() -> void:
    apply_damage.connect(
        func(info: DamageInfo) -> void:
            print("final damage: ", info.calced_damage)
    )



func _calc_damage_additions(additions: Array[float]) -> float:
    var addition := 1.0
    for add in additions:
        addition *= (1 + add)
    return addition


func _calc_resistance_factor(resistance: float) -> float:
    if resistance < 0: return 1 - (resistance * 0.5)
    elif 0 <= resistance and resistance <= 0.75: return 1 - resistance
    else: return 1 / (4 * resistance + 1)


func _calc_defense_factor(defense: float) -> float:
    # return defense / (defense + 5 * lvl + 500)
    return 100 / (100 + defense)


func _calc_real_damage(info: DamageInfo) -> void:
    var damage := info.damage
    var addition := 1.0
    var defense_factor := _calc_defense_factor(info.target.data.defense.value)
    var resistance_factor := 1.0
    var is_critical := Utils.prize_draw(info.critical_rate)

    match info.attribute:
        DamageInfo.DamageAttribute.NORMAL: 
            addition = _calc_damage_additions(info.source.data.normal_additions)
            # defense_factor = _calc_defense_factor(info.target.data.defense.value)
        DamageInfo.DamageAttribute.FIRE: 
            addition = _calc_damage_additions(info.source.data.fire_additions)
            resistance_factor = _calc_resistance_factor(info.target.data.fire_resistance.value)
        DamageInfo.DamageAttribute.ICE: 
            addition = _calc_damage_additions(info.source.data.ice_additions)
            resistance_factor = _calc_resistance_factor(info.target.data.ice_resistance.value)
        DamageInfo.DamageAttribute.THUNDER: 
            addition = _calc_damage_additions(info.source.data.thunder_additions)
            resistance_factor = _calc_resistance_factor(info.target.data.thunder_resistance.value)

    defense_factor = 1.0 if info.ignore_defense else defense_factor
    damage = damage * addition * (1 + info.critical_degree * int(is_critical)) * defense_factor * resistance_factor
    info.calced_damage = damage
    info.is_critical = is_critical



# func _calc_real_damage(info: DamageInfo) -> void:
#     var defense := info.target.data.defense.value if not info.ignore_defense else 0.0
#     var shield := info.target.data.shield.value if not info.ignore_shield else 0.0
#     shield -= info.damage
#     # 护盾没有扣除完，扣除相应的护盾
#     if shield >= 0:
#         #info.target.data.shield.value = shield
#         info.target.data.shield.add_modifier(-info.damage)
#         info.calced_damage = 0
#         return
    
#     #info.target.data.shield.value = 0.0 if not info.ignore_shield else info.target.data.shield.value
#     if not info.ignore_shield:
#         info.target.data.shield.add_modifier(-info.target.data.shield.value)
    
#     var damage := absf(shield) if not info.ignore_shield else info.damage
#     var base_damage := damage - defense
#     if base_damage <= 0:
#         info.calced_damage = 0
#         return
    
#     var critical_flag := Utils.prize_draw(info.critical_rate)
#     var critical_damage := base_damage * int(critical_flag) * info.critical_degree
#     var attr_damage := (critical_damage + base_damage) * (1 - info.get_target_attr_resistance())
#     info.calced_damage = attr_damage
#     info.is_critical = critical_flag

 

func _apply_real_damage(info: DamageInfo) -> void:
    var shield := info.target.data.shield.value if not info.ignore_shield else 0.0
    shield -= info.calced_damage
    # 护盾没有扣除完，扣除相应的护盾
    if shield >= 0:
        #info.target.data.shield.value = shield
        info.target.data.shield.add_modifier(-info.calced_damage)
        info.calced_damage = 0
        return
    
    if not info.ignore_shield:
        info.target.data.shield.add_modifier(-info.target.data.shield.value)

    info.calced_damage = absf(shield) if not info.ignore_shield else info.calced_damage

    if info.calced_damage == 0: return
    info.target.data.health.add_modifier(-info.calced_damage)


func handle_damage(info: DamageInfo) -> void:
    if not info.is_valid: return
    if info.can_miss and Utils.prize_draw(info.target.data.dodge_rate.value):
        # 未命中
        info.is_miss = true
        info.source.handle_damage_signals.send_damage_miss.emit(info)
        info.target.handle_damage_signals.receive_damage_miss.emit(info)
        miss_damage.emit(info)
        return
    
    info.source.handle_damage_signals.before_calc_send_damage.emit(info)
    info.target.handle_damage_signals.before_calc_receive_damage.emit(info)
    before_calc_damage.emit(info)
    if not info.is_valid: return
    
    _calc_real_damage(info)
    
    info.source.handle_damage_signals.after_calc_send_damage.emit(info)
    info.target.handle_damage_signals.after_calc_receive_damage.emit(info)
    after_calc_damage.emit(info)
    if not info.is_valid: return
    
    _apply_real_damage(info)
    info.source.handle_damage_signals.send_damage_applied.emit(info)
    info.target.handle_damage_signals.receive_damage_applied.emit(info)
    apply_damage.emit(info)
    
    # 一次伤害处理完毕
    info.handled_count += 1
